class Vue {
  constructor() { }
}
/**
 * 双向绑定
 * 响应式系统的简单实现
 */

let activeEffectFn = null;
const activeEffectFnStack = [];
const bucket = new WeakMap();
let ownKeysSymbo = Symbol();
const TriggerType = {
  SET: "SET",
  ADD: "ADD",
  DELETE: "DELETE",
};

function track(target, key) {
  // 每个对象下面对应的属性都有一个副作用函数
  if (!activeEffectFn) return;
  let deps = bucket.get(target);
  if (!deps) {
    bucket.set(target, (deps = new Map()));
  }
  let effectFns = deps.get(key);
  if (!effectFns) {
    deps.set(key, (effectFns = new Set()));
  }
  effectFns.add(activeEffectFn);
  activeEffectFn.deps.push(effectFns);
}
function trigger(target, key, type) {
  let deps = bucket.get(target);
  if (!deps) return;
  let effectFns = deps.get(key);
  const iteratorEffectFns = deps.get(ownKeysSymbo);
  const efectsToRun = new Set();

  // 处理ownKeys 当属性是新增或者删除的时候通过symbol(ownKeysSymbo)触发和ownKeys绑定的effect执行
  if (type === TriggerType.ADD || type === TriggerType.DELETE) {
    iteratorEffectFns &&
      iteratorEffectFns.forEach((effectFn) => {
        if (effectFn && effectFn !== activeEffectFn) {
          efectsToRun.add(effectFn);
        }
      });
  }
  effectFns &&
    effectFns.forEach((effectFn) => {
      if (effectFn && effectFn !== activeEffectFn) {
        efectsToRun.add(effectFn);
      }
    });
  efectsToRun.forEach((effectFn) => {
    if (effectFn.options && effectFn.options.schelder) {
      effectFn.options.schelder(effectFn);
    } else {
      effectFn();
    }
  });
}

let p = {};
let q = {
  bar: "1",
};
let handle = {
  get(target, key, receiver) {
    console.log('get',target, key);
    if (key === "raw") return target;
    // 在get中进行track收集依赖函数
    track(target, key);
    return Reflect.get(target, key, receiver);
  },
  set(target, key, newValue, receiver) {
    console.log(target, receiver);
    let type = Object.prototype.hasOwnProperty.call(target, key)
      ? TriggerType.SET
      : TriggerType.ADD;
    let oldValue = target[key];
    // 这里带着receiver和不带receiver会有两种不同结果
    // 1 带着receiver会在child添加bar并设置位123
    // 2 不带receiver会直接修改parent中的bar值为123
    // 可能导致这种问题的因素是 
    // 1 带着receiver他的this一直是child 第一次执行Reflect.set(target, key, newValue,receiver) 由于child上无bar 所以回去原型链上找bar 然后触发第二次set 但是此时第四个参数
    // receiver一直是child 所以最终bar会设置到child
    // 2 不带receiver的时候执行Reflect.set(target, key, newValue)由于child上没有bar所以会直接去原型链上找bar 但是并没有传入第四个参数receiver
    // 所以第二次触发set的时候当前的this是parent所以修改的就是parent的bar
    console.log(target == q, target == p, 'set', newValue);
    // let res = Reflect.set(target, key, newValue);

    let res = Reflect.set(target, key, newValue, receiver);
    console.log(p, q);
    if (receiver.raw === target) {
      if (
        newValue !== oldValue &&
        (oldValue === oldValue || newvalue === newvalue)
      ) {
        trigger(target, key, type);
      }
    }

    return res;
  },
  has(target, key, receiver) {
    // 在get中进行track收集依赖函数
    track(target, key);
    return Reflect.has(target, key, receiver);
  },
  deleteProperty(target, key, receiver) {
    let hasOwnKeys = Object.prototype.hasOwnProperty.call(target, key);
    let isDelete = Reflect.deleteProperty(target, key);
    if (isDelete && hasOwnKeys) {
      trigger(target, key, TriggerType.DELETE);
    }
    // 在get中进行track收集依赖函数
    return isDelete;
  },
  ownKeys(target) {
    track(target, ownKeysSymbo);
    return Reflect.ownKeys(target);
  },
};


// has
// 清除分支
function cleanup(activeFn) {
  for (let i = 0; i < activeFn.deps.length; i++) {
    let deps = activeFn.deps[i];
    deps.delete(activeFn);
  }
  activeFn.deps.length = 0;
}
function effect(effectFn, options) {
  function activeFn() {
    cleanup(activeFn);
    activeEffectFn = activeFn;
    activeEffectFnStack.push(activeFn);
    let res = effectFn();
    activeEffectFnStack.pop();
    activeEffectFn = activeEffectFnStack[activeEffectFnStack.length - 1];
    return res;
  }
  activeFn.deps = [];
  activeFn.options = options;
  if (options && options.lazy) {
    return activeFn;
  }
  activeFn();
}

// Object.setPrototypeOf(p, q);
// Reflect.set(p,'bar','123')
// console.log(p,q);
function reactive(obj) {
  return new Proxy(obj, handle);
}
let parent = reactive(q);
let child = reactive(p);
// 问题 当我们修改bar的时候 副作用函数会被触发两次 原因是当我们执行child.bar的时候 会触发parent的get和child的get 依赖会收集两次 当我们执行child.bar修改的时候 由于child上不存在属性bar 所以会触发chid的bar的set方法
// 在执行parent下的bar的set 所以两次set执行的target不一样 第一次的target是q 第二次的target是p 所以我们可以通过拿到receiver的原始对象来进行对比先择触发trigger
Object.setPrototypeOf(child, parent);
effect(() => {
  console.log(child.bar);
});

child.bar = "123";
